/********************************************************************* * * Copyright (C) 2002 Andrew Khan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***************************************************************************/ package jxl.read.biff; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import jxl.common.Logger; import jxl.CellReferenceHelper; import jxl.Hyperlink; import jxl.Range; import jxl.Sheet; import jxl.WorkbookSettings; import jxl.biff.IntegerHelper; import jxl.biff.RecordData; import jxl.biff.SheetRangeImpl; import jxl.biff.StringHelper; /** * A number record. This is stored as 8 bytes, as opposed to the * 4 byte RK record */ public class HyperlinkRecord extends RecordData implements Hyperlink { /** * The logger */ private static Logger logger = Logger.getLogger(HyperlinkRecord.class); /** * The first row */ private int firstRow; /** * The last row */ private int lastRow; /** * The first column */ private int firstColumn; /** * The last column */ private int lastColumn; /** * The URL referred to by this hyperlink */ private URL url; /** * The local file referred to by this hyperlink */ private File file; /** * The location in this workbook referred to by this hyperlink */ private String location; /** * The range of cells which activate this hyperlink */ private SheetRangeImpl range; /** * The type of this hyperlink */ private LinkType linkType; /** * The excel type of hyperlink */ private static class LinkType {}; private static final LinkType urlLink = new LinkType(); private static final LinkType fileLink = new LinkType(); private static final LinkType workbookLink = new LinkType(); private static final LinkType unknown = new LinkType(); /** * Constructs this object from the raw data * * @param t the raw data * @param s the sheet * @param ws the workbook settings */ HyperlinkRecord(Record t, Sheet s, WorkbookSettings ws) { super(t); linkType = unknown; byte[] data = getRecord().getData(); // Build up the range of cells occupied by this hyperlink firstRow = IntegerHelper.getInt(data[0], data[1]); lastRow = IntegerHelper.getInt(data[2], data[3]); firstColumn = IntegerHelper.getInt(data[4], data[5]); lastColumn = IntegerHelper.getInt(data[6], data[7]); range = new SheetRangeImpl(s, firstColumn, firstRow, lastColumn, lastRow); int options = IntegerHelper.getInt(data[28], data[29], data[30], data[31]); boolean description = (options & 0x14) != 0; int startpos = 32; int descbytes = 0; if (description) { int descchars = IntegerHelper.getInt (data[startpos], data[startpos + 1], data[startpos + 2], data[startpos + 3]); descbytes = descchars * 2 + 4; } startpos += descbytes; boolean targetFrame = (options & 0x80) != 0; int targetbytes = 0; if (targetFrame) { int targetchars = IntegerHelper.getInt (data[startpos], data[startpos + 1], data[startpos + 2], data[startpos + 3]); targetbytes = targetchars * 2 + 4; } startpos += targetbytes; // Try and determine the type if ((options & 0x3) == 0x03) { linkType = urlLink; // check the guid monicker if (data[startpos] == 0x03) { linkType = fileLink; } } else if ((options & 0x01) != 0) { linkType = fileLink; // check the guid monicker if (data[startpos] == (byte) 0xe0) { linkType = urlLink; } } else if ((options & 0x08) != 0) { linkType = workbookLink; } // Try and determine the type if (linkType == urlLink) { String urlString = null; try { startpos += 16; // Get the url, ignoring the 0 char at the end int bytes = IntegerHelper.getInt(data[startpos], data[startpos + 1], data[startpos + 2], data[startpos + 3]); urlString = StringHelper.getUnicodeString(data, bytes / 2 - 1, startpos + 4); url = new URL(urlString); } catch (MalformedURLException e) { logger.warn("URL " + urlString + " is malformed. Trying a file"); try { linkType = fileLink; file = new File(urlString); } catch (Exception e3) { logger.warn("Cannot set to file. Setting a default URL"); // Set a default URL try { linkType = urlLink; url = new URL("http://www.andykhan.com/jexcelapi/index.html"); } catch (MalformedURLException e2) { // fail silently } } } catch (Throwable e) { StringBuffer sb1 = new StringBuffer(); StringBuffer sb2 = new StringBuffer(); CellReferenceHelper.getCellReference(firstColumn, firstRow, sb1); CellReferenceHelper.getCellReference(lastColumn, lastRow, sb2); sb1.insert(0, "Exception when parsing URL "); sb1.append('\"').append(sb2.toString()).append("\". Using default."); logger.warn(sb1, e); // Set a default URL try { url = new URL("http://www.andykhan.com/jexcelapi/index.html"); } catch (MalformedURLException e2) { // fail silently } } } else if (linkType == fileLink) { try { startpos += 16; // Get the name of the local file, ignoring the zero character at the // end int upLevelCount = IntegerHelper.getInt(data[startpos], data[startpos + 1]); int chars = IntegerHelper.getInt(data[startpos + 2], data[startpos + 3], data[startpos + 4], data[startpos + 5]); String fileName = StringHelper.getString(data, chars - 1, startpos + 6, ws); StringBuffer sb = new StringBuffer(); for (int i = 0; i < upLevelCount; i++) { sb.append("..\\"); } sb.append(fileName); file = new File(sb.toString()); } catch (Throwable e) { logger.warn("Exception when parsing file " + e.getClass().getName() + "."); file = new File("."); } } else if (linkType == workbookLink) { int chars = IntegerHelper.getInt(data[32], data[33], data[34], data[35]); location = StringHelper.getUnicodeString(data, chars - 1, 36); } else { // give up logger.warn("Cannot determine link type"); return; } } /** * Determines whether this is a hyperlink to a file * * @return TRUE if this is a hyperlink to a file, FALSE otherwise */ public boolean isFile() { return linkType == fileLink; } /** * Determines whether this is a hyperlink to a web resource * * @return TRUE if this is a URL */ public boolean isURL() { return linkType == urlLink; } /** * Determines whether this is a hyperlink to a location in this workbook * * @return TRUE if this is a link to an internal location */ public boolean isLocation() { return linkType == workbookLink; } /** * Returns the row number of the top left cell * * @return the row number of this cell */ public int getRow() { return firstRow; } /** * Returns the column number of the top left cell * * @return the column number of this cell */ public int getColumn() { return firstColumn; } /** * Returns the row number of the bottom right cell * * @return the row number of this cell */ public int getLastRow() { return lastRow; } /** * Returns the column number of the bottom right cell * * @return the column number of this cell */ public int getLastColumn() { return lastColumn; } /** * Gets the URL referenced by this Hyperlink * * @return the URL, or NULL if this hyperlink is not a URL */ public URL getURL() { return url; } /** * Returns the local file eferenced by this Hyperlink * * @return the file, or NULL if this hyperlink is not a file */ public File getFile() { return file; } /** * Exposes the base class method. This is used when copying hyperlinks * * @return the Record data */ public Record getRecord() { return super.getRecord(); } /** * Gets the range of cells which activate this hyperlink * The get sheet index methods will all return -1, because the * cells will all be present on the same sheet * * @return the range of cells which activate the hyperlink */ public Range getRange() { return range; } /** * Gets the location referenced by this hyperlink * * @return the location */ public String getLocation() { return location; } }